import { RedisService } from '../redis/redis.service';
import { Injectable, Logger, Inject, HttpStatus, HttpException, UnauthorizedException, } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Like, Repository } from 'typeorm';
import RegisterUserDto from './dto/RegisterUserDto';
import { User } from './entities/User';
import { md5 } from 'src/common/utils';
import { Role } from './entities/Role';
import { Permission } from './entities/Permission';
import { EmailService } from 'src/modules/email/email.service';
import LoginDto from './dto/LoginDto';
import { LoginUserVo, UserInfo } from './vo/LoginUserVo'
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { PasswordDto } from './dto/PasswordDto';
import { UpdateUserDto } from './dto/UpdateUserDto';
import { RefreshTokenVo } from './vo/RefreshTokenVo';
import { UserListVo } from './vo/UserListVo';



@Injectable()
export class UserService {
    private logger = new Logger();

    @Inject(RedisService)
    private redisService: RedisService;

    @Inject(JwtService)
    private jwtService: JwtService

    @Inject(ConfigService)
    private configService: ConfigService

    @Inject(EmailService)
    private emailService: EmailService

    @InjectRepository(User)
    private userRepository: Repository<User>;

    @InjectRepository(Role)
    private roleRepository: Repository<Role>;

    @InjectRepository(Permission)
    private permissionRepository: Repository<Permission>;

    async initData() {
        const user1 = new User();
        user1.username = "zhangsan";
        user1.password = md5("111111");
        user1.email = "xxx@xx.com";
        user1.isAdmin = true;
        user1.nickName = '张三';
        user1.phoneNumber = '13233323333';

        const user2 = new User();
        user2.username = 'lisi';
        user2.password = md5("222222");
        user2.email = "yy@yy.com";
        user2.nickName = '李四';

        const role1 = new Role();
        role1.name = '管理员';

        const role2 = new Role();
        role2.name = '普通用户';

        const permission1 = new Permission();
        permission1.code = 'ccc';
        permission1.description = '访问 ccc 接口';

        const permission2 = new Permission();
        permission2.code = 'ddd';
        permission2.description = '访问 ddd 接口';

        user1.roles = [role1];
        user2.roles = [role2];

        role1.permissions = [permission1, permission2];
        role2.permissions = [permission1];

        await this.permissionRepository.save([permission1, permission2]);
        await this.roleRepository.save([role1, role2]);
        await this.userRepository.save([user1, user2]);
    }

    async register(registerUserDto: RegisterUserDto) {
        console.log(registerUserDto);

        const captcha = await this.redisService.get(`captcha_${registerUserDto.email}`);

        if (!captcha) {
            throw new HttpException('验证码已失效', HttpStatus.BAD_REQUEST);
        }

        if (registerUserDto.captcha !== captcha) {
            throw new HttpException('验证码不正确', HttpStatus.BAD_REQUEST);
        }

        const foundUser = await this.userRepository.findOneBy({
            username: registerUserDto.username,
        });

        if (foundUser) {
            throw new HttpException('用户已存在', HttpStatus.BAD_REQUEST);
        }

        const newUser = new User();
        newUser.username = registerUserDto.username;
        newUser.password = md5(registerUserDto.password);
        newUser.email = registerUserDto.email;
        newUser.nickName = registerUserDto.nickName;

        try {
            await this.userRepository.save(newUser);
            return {
                code: 200,
                msg: '注册成功',
            };
        } catch (e) {
            this.logger.error(e, UserService);
            return {
                code: 500,
                msg: '注册失败',
            };
        }
    }

    async captcha(address: string) {
        const code = Math.random().toString().slice(2, 8)
        this.redisService.set(`captcha_${address}`, code, 5 * 60)
        await this.emailService.sendMail({
            to: address,
            subject: '注册验证码',
            html: `<p>你的注册验证码是${code}</p>`
        })
    }
    async updatePasswordCaptcha(address: string) {
        const code = Math.random().toString().slice(2, 8)
        this.redisService.set(`update_password_captcha_${address}`, code, 5 * 60)
        await this.emailService.sendMail({
            to: address,
            subject: '更改密码验证码',
            html: `<p>你的更改密码验证码是${code}</p>`
        })
    }
    async updateCaptcha(address: string) {
        const code = Math.random().toString().slice(2, 8)
        this.redisService.set(`update_user_captcha_${address}`, code, 5 * 60)
        await this.emailService.sendMail({
            to: address,
            subject: '更改用户信息验证码',
            html: `<p>你的验证码是 ${code}</p>`
        });
    }

    async login(loginDto: LoginDto, isAdmin: boolean = false): Promise<LoginUserVo> {
        const user = await this.findUserBy({
            username: loginDto.username,
            isAdmin
        })

        if (user.password !== md5(loginDto.password)) {
            throw new HttpException('用户名或密码错误', HttpStatus.BAD_REQUEST)
        }

        return this.generateUserVo(user)
    }

    async refresh(refreshToken: string, isAdmin: boolean) {
        try {
            const data = this.jwtService.verify(refreshToken)
            const user = await this.findUserBy({
                id: data.userId,
                isAdmin
            })
            const loginUserVo = this.generateUserVo(user)
            const refreshTokenVo = new RefreshTokenVo()
            refreshTokenVo.access_token = loginUserVo.accessToken
            refreshTokenVo.refresh_token = loginUserVo.refreshToken
            return refreshTokenVo
        } catch (e) {
            throw new UnauthorizedException('token 已失效，请重新登录');
        }
    }

    async findUserBy(queryParams: { [key: string]: any }) {
        const user = await this.userRepository.findOne({
            where: { ...queryParams },
            relations: ['roles', 'roles.permissions']
        })
        if (!user) {
            throw new HttpException('用户名或密码错误', HttpStatus.BAD_REQUEST)
        }
        return user
    }

    generateUserVo(user: User) {

        const loginUserVo = new LoginUserVo();
        loginUserVo.userInfo = {
            id: user.id,
            username: user.username,
            nickName: user.nickName,
            email: user.email,
            phoneNumber: user.phoneNumber,
            headPic: user.headPic,
            createTime: user.createTime.getTime(),
            isFrozen: user.isFrozen,
            isAdmin: user.isAdmin,
            roles: user.roles.map(item => item.name),
            permissions: user.roles.reduce((arr, item) => {
                item.permissions.forEach(permission => {
                    if (arr.indexOf(permission) === -1) {
                        arr.push(permission);
                    }
                })
                return arr;
            }, [])
        }
        const { accessToken, refreshToken } = this.generateToken(loginUserVo.userInfo)
        loginUserVo.accessToken = accessToken
        loginUserVo.refreshToken = refreshToken
        return loginUserVo
    }
    generateToken(userInfo: { id: number, username: string, roles: string[], permissions: string[], [key: string]: any }) {
        const accessToken = this.jwtService.sign({
            userId: userInfo.id,
            username: userInfo.username,
            roles: userInfo.roles,
            permissions: userInfo.permissions
        }, {
            expiresIn: this.configService.get("jwt_access_token_expires_time") || '30m'
        })
        const refreshToken = this.jwtService.sign({
            userId: userInfo.id
        }, {
            expiresIn: this.configService.get('jwt_refresh_token_expres_time') || '7d'
        })
        return { accessToken, refreshToken }
    }

    async findUserDetailById(userId: number) {
        return await this.userRepository.findOne({
            where: {
                id: userId
            }
        })
    }

    async updatePassword(passwordDto: PasswordDto) {
        const password_captcha = await this.redisService.get(`update_password_captcha_${passwordDto.email}`)
        if (!password_captcha) {
            throw new HttpException('验证码已失效', HttpStatus.BAD_REQUEST)
        }
        if (password_captcha !== passwordDto.captcha) {
            throw new HttpException('验证码错误', HttpStatus.BAD_REQUEST)
        }
        const foundUser = await this.userRepository.findOne({
            where: {
                username: passwordDto.username
            }
        })
        if (foundUser.email !== passwordDto.email) {
            throw new HttpException('邮箱错误', HttpStatus.BAD_REQUEST)
        }
        foundUser.password = md5(passwordDto.password)
        try {
            await this.userRepository.save(foundUser)
            return {
                code: 200,
                msg: '密码修改成功'
            }
        } catch (e) {
            this.logger.error(e, UserService)
            return {
                code: 500,
                msg: '密码修改失败'
            }
        }

    }

    async update(userId: number, updateUserDto: UpdateUserDto) {
        const captcha = await this.redisService.get(`update_user_captcha_${updateUserDto.email}`)
        if (!captcha) {
            throw new HttpException('验证码已失效', HttpStatus.BAD_REQUEST)
        }
        if (captcha !== updateUserDto.captcha) {
            throw new HttpException('验证码不正确', HttpStatus.BAD_REQUEST);
        }
        const foundUser = await this.userRepository.findOne({
            where: {
                id: userId
            }
        })

        if (updateUserDto.nickName) {
            foundUser.nickName = updateUserDto.nickName;
        }
        if (updateUserDto.headPic) {
            foundUser.headPic = updateUserDto.headPic;
        }

        try {
            this.userRepository.save(foundUser)
            return {
                code: 200,
                msg: '用户信息修改成功'
            }
        } catch (e) {
            this.logger.error(e, UserService)
            return {
                code: 500,
                msg: '用户信息修改失败'
            }
        }
    }

    async freeze(userId: number) {
        const user = await this.userRepository.findOneBy({
            id: userId
        })
        user.isFrozen = true
        await this.userRepository.save(user)
    }

    async findUserByPage(pageNo: number, pageSize: number) {
        const skipCount = (pageNo - 1) * pageSize
        const [users, totalCount] = await this.userRepository.findAndCount({
            select: ['id', 'username', 'nickName', 'email', 'phoneNumber', 'isFrozen', 'headPic', 'createTime'],
            skip: skipCount,
            take: pageSize
        })
        return {
            users,
            totalCount
        }
    }

    async findUsers(username: string, nickName: string, email: string, pageNo: number, pageSize: number) {
        const skipCount = (pageNo - 1) * pageSize;

        const condition: Record<string, any> = {};

        if (username) {
            condition.username = Like(`%${username}%`);
        }
        if (nickName) {
            condition.nickName = Like(`%${nickName}%`);
        }
        if (email) {
            condition.email = Like(`%${email}%`);
        }

        const [users, totalCount] = await this.userRepository.findAndCount({
            select: ['id', 'username', 'nickName', 'email', 'phoneNumber', 'isFrozen', 'headPic', 'createTime'],
            skip: skipCount,
            take: pageSize,
            where: condition
        });
        const userListVo = new UserListVo()
        userListVo.users = users
        userListVo.totalCount = totalCount
        return userListVo
    }
}
